/**************************************************
* 创建作者：	咕咕咕
* 创建时间：	2020-12-13
* 作用描述：	#计算器
***************************************************/

using System.Collections.Generic;
using Utils.Calc;

namespace Utils
{
	public class Calculator
	{
		#region ----字段----
		private readonly Stack<Operator> optrs	= new Stack<Operator>();
		private readonly Stack<double> opnds	= new Stack<double>();
		private readonly CalcChar charBuf		= new CalcChar();			//操作的字符
		private readonly OperandBuffer buffer	= new OperandBuffer();		//操作数缓冲
		#endregion

		#region ----公有方法----
		//开始计算
		public double Process(string inputStr)
		{
			int index = 0;                  //字符下标
			Operator optr;                  //运算符

			while (index < inputStr.Length)
			{
				if (!charBuf.Overlay(inputStr[index]))
				{
					index++;
					continue;
				}

				if (OperandBuffer.IsNumber(charBuf.Cell))
					buffer.Append(charBuf.Cell);
				else
				{
					PushOpnds(buffer);
					if (Operator.IsOperator(charBuf.Cell))
					{
						optr = OperatorFactory.Create(charBuf);
						if (optr.Priority != 0)
						{
							Calculate(optr);
						}
						optrs.Push(optr);
					}
					else if (Delimiter.IsRight(charBuf.Cell))
					{
						Calculate();
						optrs.Pop();
					}
				}
				index++;
			}

			PushOpnds(buffer);
			Calculate();
			return opnds.Pop();
		}
		#endregion

		#region ----私有方法----
		private void Calculate()
        {
			//一直计算到没有运算符了或者遇到左括号
            while (optrs.Count > 0 && optrs.Peek().Priority != 0)
            {
				if (optrs.Peek().TwoOperand)
					opnds.Push(optrs.Pop().Calculate(opnds.Pop(), opnds.Pop()));
				else
					opnds.Push(optrs.Pop().Calculate(opnds.Pop()));
			}
        }

		private void Calculate(Operator optr)
        {
			//一直计算到没有运算符或者遇到左括号或者前置运算符但没有数可操作
			while (optrs.Count > 0 && optrs.Peek().Priority != 0 && optr.Priority <= optrs.Peek().Priority && (!optrs.Peek().Praevia || (optrs.Peek().Praevia && opnds.Count > 0)))
			{
				if (optrs.Peek().TwoOperand)
					opnds.Push(optrs.Pop().Calculate(opnds.Pop(), opnds.Pop()));
				else
					opnds.Push(optrs.Pop().Calculate(opnds.Pop()));
			}
		}

		//操作数入栈
		private void PushOpnds(OperandBuffer buffer)
        {
			if (buffer.Length > 0)
			{
				opnds.Push(buffer.Number);
				buffer.Clear();
			}
		}
		#endregion

	}
}